/**
 * Created by yuchenfei on 2016/12/6.
 */

var ParticleSystem = {
    init: function () {
        ParticleSystem.Drawing.init('.canvas');
        ParticleSystem.Drawing.loop(function () {  // 开始底层绘图循环
            ParticleSystem.Shape.render();  // 依据形状绘点
        });
    }
};

ParticleSystem.Drawing = (function () {
    var canvas,
        context,
        renderFn,
        requestFrame =  // 按浏览器支持使用动画框架
            window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback) {
                window.setTimeout(callback, 1000 / 60);
            };

    return {
        init: function (el) {
            canvas = document.querySelector(el);
            context = canvas.getContext('2d');
            this.adjustCanvas();

            window.addEventListener('resize', function (e) {
                ParticleSystem.Drawing.adjustCanvas();
            });
        },

        loop: function (fn) {  // 循环回调
            renderFn = !renderFn ? fn : renderFn;
            this.clearFrame();
            renderFn();
            requestFrame.call(window, this.loop.bind(this));
        },

        adjustCanvas: function () {  // 画布大小自适应
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        },

        clearFrame: function () {  // 清屏
            context.clearRect(0, 0, canvas.width, canvas.height);
        },

        getArea: function () {
            return {w: canvas.width, h: canvas.height};
        },

        drawCircle: function (point, c) {  // 调用绘图API画点
            context.fillStyle = c.render();
            context.beginPath();
            context.arc(point.x, point.y, point.d, 0, 2 * Math.PI, true);
            context.closePath();
            context.fill();
        }
    }
}());

ParticleSystem.Point = function (args) {
    this.x = args.x;  // x坐标
    this.y = args.y;  // y坐标
    this.d = args.d;  // 半径
    this.a = args.a;  // 透明度
    this.h = args.h;  // 伸缩率
};

ParticleSystem.Color = function (r, g, b, a) {
    this.r = r;
    this.g = g;
    this.b = b;
    this.a = a;
};

ParticleSystem.Color.prototype = {
    render: function () {
        return 'rgba(' + this.r + ',' + +this.g + ',' + this.b + ',' + this.a + ')';
    }
};

ParticleSystem.Dot = function (x, y) {
    this.point = new ParticleSystem.Point({
        x: x,  // x坐标
        y: y,  // y坐标
        d: 5,  // 半径
        a: 1,  // 透明度
        h: 0  // 缩放率
    });

    this.e = 0.07;
    this.s = true;  // 状态

    this.c = new ParticleSystem.Color(200, 200, 200, this.point.a);  // 颜色

    this.t = this.clone();
    this.q = [];  // 目标点列表
};

ParticleSystem.Dot.prototype = {
    clone: function () {
        return new ParticleSystem.Point({
            x: this.x,
            y: this.y,
            d: this.d,
            a: this.a,
            h: this.h
        });
    },

    _draw: function () {  // 调用Drawing画点
        this.c.a = this.point.a;
        ParticleSystem.Drawing.drawCircle(this.point, this.c);
    },

    _moveTowards: function (n) {  // 向目标点移动
        var details = this.distanceTo(n, true),
            dx = details[0],
            dy = details[1],
            d = details[2],
            e = this.e * d;

        if (this.point.h === -1) {
            this.point.x = n.x;
            this.point.y = n.y;
            return true;
        }

        if (d > 1) {
            this.point.x -= ((dx / d) * e);
            this.point.y -= ((dy / d) * e);
        } else {
            if (this.point.h > 0) {
                this.point.h--;
            } else {
                return true;
            }
        }

        return false;
    },

    _update: function () {  // 更新点
        if (this._moveTowards(this.t)) {
            var p = this.q.shift();

            if (p) {
                this.t.x = p.x || this.point.x;
                this.t.y = p.y || this.point.y;
                this.t.d = p.d || this.point.d;
                this.t.a = p.a || this.point.a;
                this.point.h = p.h || 0;
            } else {
                if (this.s) {
                    this.point.x -= Math.sin(Math.random() * 3.142);
                    this.point.y -= Math.sin(Math.random() * 3.142);
                } else {
                    this.move(new ParticleSystem.Point({
                        x: this.point.x + (Math.random() * 50) - 25,
                        y: this.point.y + (Math.random() * 50) - 25,
                    }));
                }
            }
        }

        d = this.point.a - this.t.a;
        this.point.a = Math.max(0.1, this.point.a - (d * 0.05));
        d = this.point.d - this.t.d;
        this.point.d = Math.max(1, this.point.d - (d * 0.05));
    },

    distanceTo: function (n, details) {  // 返回与目标点的距离 n:目标点 d:是否返回细节
        var dx = this.point.x - n.x,
            dy = this.point.y - n.y,
            d = Math.sqrt(dx * dx + dy * dy);

        return details ? [dx, dy, d] : d;
    },

    move: function (p, avoidStatic) {
        if (!avoidStatic || (avoidStatic && this.distanceTo(p) > 1)) {
            this.q.push(p);
        }
    },

    render: function () {  // 调用底层函数画点
        this._update();
        this._draw();
    }
}

ParticleSystem.ShapeBuilder = (function () {
    var gap = 13,  // minimum:10 default:13
        shapeCanvas = document.createElement('canvas'),
        shapeContext = shapeCanvas.getContext('2d'),
        fontSize = 500,
        fontFamily = 'Avenir, Helvetica Neue, Helvetica, Arial, sans-serif';

    function fit() {
        shapeCanvas.width = Math.floor(window.innerWidth / gap) * gap;
        shapeCanvas.height = Math.floor(window.innerHeight / gap) * gap;
        shapeContext.fillStyle = 'red';
        shapeContext.textBaseline = 'middle';
        shapeContext.textAlign = 'center';
    }

    function processCanvas() {  // 分析形状画布，获取点集
        var dots = [],
            pixels,
            x = 0,
            y = 0,
            fx = shapeCanvas.width,
            fy = shapeCanvas.height,
            w = 0,
            h = 0;

        // 使用getImageData获取画布图像像素信息
        pixels = shapeContext.getImageData(0, 0, shapeCanvas.width, shapeCanvas.height).data;

        // 分析像素信息
        for (var p = 0; p < pixels.length; p += (4 * gap)) {
            if (pixels[p + 3] > 0) {
                dots.push(new ParticleSystem.Point({
                    x: x,
                    y: y
                }));

                w = x > w ? x : w;
                h = y > h ? y : h;
                fx = x < fx ? x : fx;
                fy = y < fy ? y : fy;
            }

            x += gap;

            if (x >= shapeCanvas.width) {
                x = 0;
                y += gap;
                p += gap * 4 * shapeCanvas.width;
            }
        }

        return {dots: dots, w: w + fx, h: h + fy};
    }

    function setFontSize(s) {
        shapeContext.font = 'bold ' + s + 'px ' + fontFamily;
    }

    function isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

    function init() {
        fit();
        window.addEventListener('resize', fit);
    }

    init();

    return {
        letter: function (l) {
            var s = 0;

            setFontSize(fontSize);
            s = Math.min(fontSize,
                (shapeCanvas.width / shapeContext.measureText(l).width) * 0.8 * fontSize,
                (shapeCanvas.height / fontSize) * (isNumber(l) ? 1 : 0.45) * fontSize);
            setFontSize(s);

            shapeContext.clearRect(0, 0, shapeCanvas.width, shapeCanvas.height);
            shapeContext.fillText(l, shapeCanvas.width / 2, shapeCanvas.height / 2);

            return processCanvas();
        }
    };
}());

ParticleSystem.Shape = (function () {
    var dots = [],  // 保存图形点集
        width = 0,
        height = 0,
        cx = 0,
        cy = 0;

    function compensate() {  // 根据画布大小计算中心
        var area = ParticleSystem.Drawing.getArea();

        cx = area.w / 2 - width / 2;
        cy = area.h / 2 - height / 2;
    }

    return {
        shuffleIdle: function (n) {  // 随机放点 n：点的个数
            var area = ParticleSystem.Drawing.getArea();

            for (var d = 0; d <= n; d++) {
                dots.push(new ParticleSystem.Dot(area.w / 2, area.h / 2));
            }

            for (var i = 0; i < dots.length; i++) {
                dots[i].s = true;
                if (dots[i].s) {
                    dots[i].move(new ParticleSystem.Point({
                        d: Math.random() * 20 + 10,  // 放大点
                        a: Math.random(),
                        h: 20
                    }));

                    dots[i].s = false;
                    dots[i].e = 0.04;
                    dots[i].move(new ParticleSystem.Point({
                        x: Math.random() * area.w,
                        y: Math.random() * area.h,
                        a: 0.3, //.4
                        d: Math.random() * 4,
                        h: 0
                    }));
                }
            }
        },

        switchShape: function (n, fast) {  // （绘图关键）切换图形n:[in]ShapeBuilder返回的点集； fast<in>
            var size,
                area = ParticleSystem.Drawing.getArea();

            width = n.w;
            height = n.h;

            compensate();

            if (n.dots.length > dots.length) {  // 需要的点比列表中的点多
                size = n.dots.length - dots.length;  // 计算差多少点
                for (var d = 1; d <= size; d++) {  // 生成相应数量的点
                    dots.push(new ParticleSystem.Dot(area.w / 2, area.h / 2));
                }
            }

            var d = 0, i = 0;

            while (n.dots.length > 0) {
                i = Math.floor(Math.random() * n.dots.length);  // 随机数，下取整
                dots[d].e = fast ? 0.25 : (dots[d].s ? 0.14 : 0.11);  // ?速度

                if (dots[d].s) {
                    dots[d].move(new ParticleSystem.Point({
                        d: Math.random() * 20 + 10,  // 10~30
                        a: Math.random(),  // 0~1
                        h: 18
                    }));
                } else {
                    dots[d].move(new ParticleSystem.Point({
                        d: Math.random() * 5 + 5,  // 5~10
                        h: fast ? 18 : 30
                    }));
                }

                dots[d].s = true;
                dots[d].move(new ParticleSystem.Point({
                    x: n.dots[i].x + cx,
                    y: n.dots[i].y + cy,
                    a: 1,
                    d: 5,
                    h: 0
                }));

                n.dots = n.dots.slice(0, i).concat(n.dots.slice(i + 1));  // 点列表中去除当前点
                d++;
            }

            for (var i = d; i < dots.length; i++) {
                if (dots[i].s) {
                    dots[i].move(new ParticleSystem.Point({
                        d: Math.random() * 20 + 10,  // 放大点
                        a: Math.random(),
                        h: 20
                    }));

                    dots[i].s = false;
                    dots[i].e = 0.04;
                    dots[i].move(new ParticleSystem.Point({
                        x: Math.random() * area.w,
                        y: Math.random() * area.h,
                        a: 0.3, //.4
                        d: Math.random() * 4,
                        h: 0
                    }));
                }
            }
        },

        render: function () {
            for (var d = 0; d < dots.length; d++) {
                dots[d].render();
            }
        }
    }
}());
